//
//  FFBindSendCenter.m
//  FFMVVM
//
//  Created by AKsoftware on 2016/11/1.
//  Copyright © 2016年 AKsoftware. All rights reserved.
//

#import "FFKVOBindSendCenter.h"
#import "FFBindUnit.h"

@interface FFKVOBindSendCenter ()

@property (strong, nonatomic, readonly) NSMapTable *bindTable;

@end

@implementation FFKVOBindSendCenter

+ (instancetype)shareInstance
{
    static FFKVOBindSendCenter *share;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        share = [[self alloc] init];
    });
    return share;
}

- (instancetype)init
{
    if (self = [super init]) {
        _bindTable = [NSMapTable strongToWeakObjectsMapTable];
    }
    return self;
}

- (void)addUnit:(FFBindUnit *)unit
{
     @synchronized (self){
         [self.bindTable setObject:unit forKey:unit];
     }
}

- (void)removeUnit:(FFBindUnit *)unit
{
     @synchronized (self){
    [self.bindTable removeObjectForKey:unit];
     }
}

- (void)bindUnit:(FFKVOBindUnit *)unit
{
    [self destoryBindUnit:unit];
    if (unit.target) {
        @synchronized (self) {
            [self addUnit:unit];
        }
        for (NSString *key in unit.keyPathArray) {
            [unit.target addObserver:self forKeyPath:key options:unit.options context:(void *)unit];
        }
    }
}

- (void)destoryBindUnit:(FFKVOBindUnit *)unit
{
    FFKVOBindUnit *valueUnit = [self.bindTable objectForKey:unit];
    if (valueUnit && valueUnit.target) {
        for (NSString *key in valueUnit.keyPathArray) {
            [valueUnit.target removeObserver:self forKeyPath:key context:(void *)unit];
        }
    }
    [self removeUnit:unit];
}


//kvo

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    FFKVOBindUnit *unit;
    @synchronized (self) {
       unit = [self.bindTable objectForKey:(__bridge id)(context)];
    }
    [unit perfomKvoBlock:keyPath object:[object valueForKeyPath:keyPath]];
}

- (void)removeObserver:(NSObject *)observer target:(NSObject *)target indentify:(NSString *)indentify
{
    NSAssert(indentify.length > 0, @"indentify must has content");
    [self removeUnitWithCondication:^BOOL(FFBindUnit *unit) {
        return unit.observer == observer && target == unit.target && indentify == unit.indentify;
    }];
}

- (void)removeObserver:(NSObject *)observer target:(NSObject *)target
{
    [self removeUnitWithCondication:^BOOL(FFBindUnit *unit) {
        return unit.observer == observer && target == unit.target;
    }];
}

- (void)removeObserver:(NSObject *)observer
{
   [self removeUnitWithCondication:^BOOL(FFBindUnit *unit) {
       return unit.observer == observer;
   }];
}


- (void)removeUnitWithCondication:(BOOL(^)(FFBindUnit *unit))condication
{
    @synchronized (self){
        NSMutableArray *removeUnitArray = [NSMutableArray array];
        for (FFBindUnit *unit in self.bindTable) {
            if (condication(unit)|| unit.target == nil || unit.observer == nil) {
                [removeUnitArray addObject:unit];
            }
        }
        for (FFBindUnit *unit in removeUnitArray) {
            [self removeUnit:unit];
        }
    }
}

@end
